import json
import subprocess

import sys


class MCPClient:
    def __init__(self, server_command):
        # 保存服务器端的启动命令
        self.server_command = server_command
        # 把服务器进程对象初始化为None
        self.server_process = None
        # 工具列表初始为空
        self.tools = []

    def connect(self):
        try:
            # 启动服务器子进程，重定向标准输入输出 和错误输出
            self.server_process = subprocess.Popen(
                self.server_command,  # 启动服务器进程的命令 python server.py
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,  # 设置为文本模式
                bufsize=1,  # 设置缓存区的大小
            )
            # 获取服务器支持的工具列表
            self._get_tools()
            print("成功连接到服务器")
            return True
        except Exception as e:
            print(f"连接服务器失败:{str(e)}")

    def _get_tools(self):
        # 构造请求体，请求工具列表
        request = {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/list",
            "params": {},
        }
        response = self._send_request(request)
        if "result" in response:
            self.tools = response["result"].get("tools", [])
            print(f"服务器支持{len(self.tools)}个工具")
            for tool in self.tools:
                print(f"- {tool.get('description')}")

    def _send_request(self, request):
        try:
            # 先将请求对象序列化成JSON字符串
            request_str = json.dumps(request, ensure_ascii=False)
            # 写入服务器进程标准输入
            self.server_process.stdin.write(request_str + "\n")
            # 刷新输入缓存区，确保数据立刻发送
            self.server_process.stdin.flush()
            print("请求已经发送")
            # 读取服务器进程的标准输出
            response_line = self.server_process.stdout.readline()
            print("获取服务器响应")
            # 如果没有读到响应，则返回错误信息
            if not response_line:
                return {
                    "jsonrpc": "2.0",
                    "error": {"code": -32000, "message": "服务器无响应"},
                    "id": request["id"],
                }
            return json.loads(response_line.strip())

        except Exception as e:
            return {
                "jsonrpc": "2.0",
                "error": {"code": -32000, "message": f"请求失败{str(e)}"},
                "id": request["id"],
            }

    def call_tool(self, tool_name, arguments):
        import time

        request_id = int(time.time() * 1000)
        request = {
            "jsonrpc": "2.0",
            "id": request_id,
            "method": "tools/call",
            "params": {"name": tool_name, "arguments": arguments},
        }
        result = self._send_request(request)
        return result

    def _print_result(self, result):
        print(f"结果:{result}")
        if "result" in result:
            content = result["result"].get("content", [])
            for i, item in enumerate(content):
                if item.get("type") == "text":
                    print(f"内容为{i+1}:{item['text']}")
                else:
                    print(f"内容为{i+1}:{item}")

    def interactive_mode(self):
        print("可用命令:")
        print("  quit 退出程序")
        print("  list_tools 列表所有的工具")
        print("  get_weather <城市名>")
        print("  read_file <文件路径>")
        while True:
            try:
                command = input("\n请输入命令:").strip()
                if command == "quit":
                    break
                elif command == "list_tools":
                    for i, tool in enumerate(self.tools, 1):
                        print(f"{i}. {tool.get('description')}")
                elif command.startswith("read_file"):
                    file_path = command[10:].strip()
                    result = self.call_tool("read_file", {"file_path": file_path})
                    self._print_result(result)
                else:
                    print("未知命令")
            except Exception as e:
                print(f"执行命令出错:{str(e)}")

    # 断开与服务器的连接
    def disconnect(self):
        # 如果服务器进程存在
        if self.server_process:
            # 终止服务器进程
            self.server_process.terminate()
            # 等待服务器进程结束
            self.server_process.wait()
            # 打印断开连接信息
            print("👋 已断开与服务器的连接")


# python client.py server.py     node process.argv
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("使用方法: python client.py <服务器的本地路径>")
        print("示例: python client.py server.py")
        sys.exit(1)
    # 获取服务器的脚本路径
    server_script = sys.argv[1]
    # 创建客户端的实例 sys.executable=python.exe  server_script=server.py python server.py
    mcpClient = MCPClient([sys.executable, server_script])
    # 连接mcp服务器
    if mcpClient.connect():
        # 连接上之后进入interactive_mode 和用户进行通信，执行用户的命令
        mcpClient.interactive_mode()
    # 最后如果用户选择退出交互模式的话，就退出与mcp客户端的连接
    mcpClient.disconnect()
